/********************************************************/
/*	Copyright (C) 2016 Gong Li Bin		 	*/
/*	Project:	GoLib-1.0.0			*/
/*	Author:		gong_libin			*/
/*	Date:		2016_01_01			*/
/*	File:		GoConf.go			*/
/********************************************************/

package GoCls

import (
	"io"
	"os"
	"bufio"
	"GoGlb"
)

const (
	GO_CF_SECL	byte = '['
	GO_CF_SECR	byte = ']'
	GO_CF_EQUL	byte = '='
	GO_CF_TARK	byte = '#'
	GO_CF_BLNK	byte = ' '
	GO_CF_CTLT	byte = '\t'
	GO_CF_CTLR	byte = '\r'
	GO_CF_CTLN	byte = '\n'
)

type GoConf struct {
	item	int
	file	*os.File
	bior	*bufio.Reader
	pair	[64]struct {sec, key, val []byte}
}

func (c *GoConf) GoConfClose() {
	c.file.Close()
}

func (c *GoConf) GoConfOpen(name string) (err error) {
	c.file, err = os.Open(name)
	if nil == err {
		c.bior = bufio.NewReader(c.file)
	}
	return err
}

func (c *GoConf) GoConfParse() {
	for {
		str, err := c.bior.ReadString('\n')
		if io.EOF != err {
			c.GoConfSplit(str)
		} else {
			break
		}
	}
}

func (c *GoConf) GoConfSplit(str string) {
	h, t := c.GoConfTrimAll(str)
	if t > h {
		buf := []byte(str)[h:t + 1]
		if true == c.GoConfIsValid(buf) {
			if GO_CF_SECL != buf[0] {
				c.pair[c.item].key, c.pair[c.item].val = c.GoConfKeyVal(buf)
				if 0 != len(c.pair[c.item].key) && 0 != len(c.pair[c.item].val) {
					if 0 == len(c.pair[c.item].sec) && c.item > 0 {
						c.pair[c.item].sec = c.pair[c.item - 1].sec
					}
					c.item += 1
				}
			} else {
				i := len(buf) - 1
				if GO_CF_SECR == buf[i] {
					c.pair[c.item].sec = buf[1:i]
				}
			}
		}
	}
}

func (c *GoConf) GoConfKeyVal(buf []byte) (k, v []byte) {
	i, j, l := 0, 0, len(buf)
	for _, b := range buf {
		if GO_CF_EQUL != b {
			i += 1
			j += 1
		} else {
			break
		}
	}
	if 0 < i && i < l {
		// key
		for ; 0 <= i; i -- {
			if GO_CF_CTLT != buf[i] && GO_CF_BLNK != buf[i] && GO_CF_EQUL != buf[i] {
				break
			}
		}
		k = buf[0:i + 1]
		// val
		for j += 1; j < l; j++ {
			if GO_CF_CTLT != buf[j] && GO_CF_BLNK != buf[j] && GO_CF_EQUL != buf[i] {
				break
			}
		}
		v = buf[j:l]
	}
	return k, v
}

func (c *GoConf) GoConfRange(iter int) (sec, key, val []byte) {
	if 0 < iter && iter < c.item {
		sec = c.pair[iter].sec
		key = c.pair[iter].key
		val = c.pair[iter].val
	}
	return sec, key, val
}

func (c *GoConf) GoConfDisplay() {
	for i := 0; i < c.item; i ++ {
		GoGlb.GoPrint("[%s]: %s -> %s\n", c.pair[i].sec, c.pair[i].key, c.pair[i].val)
	}
}

func (c *GoConf) GoConfIsValid(buf []byte) bool {
	ret := GoGlb.GO_TRUE
	if GO_CF_TARK == buf[0] || GO_CF_CTLR == buf[0] || GO_CF_CTLN == buf[0] {
		ret = GoGlb.GO_FALSE
	}
	return ret
}

func (c *GoConf) GoConfTrimAll(str string) (h, t int ) {
	return c.__ConfTrimHead(str), c.__ConfTrimTail(str)
}

func (c *GoConf) __ConfTrimHead(str string) (h int) {
	for _, b := range []byte(str) {
		if GO_CF_CTLR == b || GO_CF_CTLN == b || GO_CF_BLNK == b || GO_CF_CTLT == b {
			h += 1
		} else {
			break
		}
	}
	return h
}

func (c *GoConf) __ConfTrimTail(str string) (t int) {
	i := 0
	for _, b := range []byte(str) {
		if GO_CF_TARK == b {
			break
		}
		i += 1
	}
	for i -= 1; 0 <= i; i -- {
		if GO_CF_CTLR != []byte(str)[i] && GO_CF_CTLN != []byte(str)[i] && GO_CF_CTLT != []byte(str)[i] && GO_CF_BLNK != []byte(str)[i] {
			break
		}
	}
	return i
}
